﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Xml;
using OSGeo.MapGuide;
using System.IO;
using System.Configuration;

namespace IGIS.Core
{
    public class HtzMaintenanceHelper
    {
        // Fields
        private const string __className = "FIELDS_ORDER";
        private const string __fnFeatureClass = "FEAT_CLASS_NAME";
        private const string __fnId = "ID";
        private const string __fnOrderId = "ORDER_ID";
        private const string __fnPropertyName = "PROPERTY_NAME";
        private readonly ModuleConfig _config;
        private readonly HtzMgHelper _helper;
        private Dictionary<string, string> _infraTypes = new Dictionary<string, string>();
        private readonly List<MgResourceIdentifier> _layersResId;
        private readonly string _maintenancePath;
        private bool _newLayerAttached;
        private readonly MgLayer _parentLayer;
        private readonly MgResourceIdentifier _sdfResId;
        private readonly string _uidKey;
        private const string STR_InfrastructureTypesFile = "MaintenanceConfig.xml";

        // Methods
        public HtzMaintenanceHelper(HtzMgHelper helper, MgLayer parentLayer, string uidKey)
        {
            this.ModuleAllowed = true;
            this.InitDefaultInfraTypes();
            this._helper = helper;
            this._uidKey = string.IsNullOrEmpty(uidKey) ? "UID" : uidKey;
            this._parentLayer = parentLayer;
            //if (this._helper.Configuration != null)
            //{
            //    this._config = this._helper.Configuration.GetProperties("FeatureCard");
            //    Dictionary<string, string> tmp = new Dictionary<string, string>();
            //    foreach (KeyValuePair<string, string> item in this._infraTypes)
            //    {
            //        if (!this._config.ContainsKey("ShowMaintFor" + item.Key))
            //        {
            //            this._helper.Configuration.RegisterPropertyDefinition("FeatureCard", "ShowMaintFor" + item.Key, item.Value, ConfigPropertyType.Boolean, "תחזוקה - הצגת שכבות", false);
            //        }
            //        else if ((bool)this._config["ShowMaintFor" + item.Key].Value)
            //        {
            //            tmp.Add(item.Key, item.Value);
            //        }
            //    }
            //    this._infraTypes = tmp;
            //}
            string mapPath = this._helper.Map.MapDefinition.Path;
            if (mapPath.LastIndexOf("/Maps") < 0)
            {
                this.ModuleAllowed = false;
            }
            else
            {
                this._maintenancePath = mapPath.Substring(0, mapPath.IndexOf("/Maps"));
                this._sdfResId = new MgResourceIdentifier(string.Format("Library://{0}/Maintenance/Data/Maintenance.FeatureSource", this._maintenancePath));
                this._layersResId = new List<MgResourceIdentifier>();
                if (!this._helper.ResourceService.ResourceExists(this._sdfResId))
                {
                    this.CreateSdf(this._sdfResId);
                }
                foreach (KeyValuePair<string, string> item in this._infraTypes)
                {
                    MgResourceIdentifier resId = new MgResourceIdentifier(string.Format("Library://{0}/Maintenance/Layers/Maint_{1}.LayerDefinition", this._maintenancePath, item.Key));
                    this._layersResId.Add(resId);
                    if (!this._helper.ResourceService.ResourceExists(resId))
                    {
                        this.CreateLayer(resId, string.Format("InfrastructureType='{0}'", item.Value));
                    }
                }
                this.AttachLayers();
                this._infraTypes.Add("none", "בחר מרשימה");
            }
        }

        private void AttachLayers()
        {
            MgLayerCollection layers = this._helper.Map.GetLayers();
            for (int i = this._layersResId.Count - 1; i >= 0; i--)
            {
                if (!layers.Contains(this._layersResId[i].Name))
                {
                    string legendLable = this._infraTypes[this._layersResId[i].GetName().Split(new char[] { '_' })[1]];
                    MgLayer layer = new MgLayer(this._layersResId[i], this._helper.ResourceService);
                    layer.SetName(this._layersResId[i].Name);
                    layer.SetLegendLabel(legendLable);
                    layer.SetDisplayInLegend(false);
                    layer.SetSelectable(false);
                    this._helper.Map.GetLayers().Insert(0, layer);
                    this._helper.Map.Save(this._helper.ResourceService);
                    this._newLayerAttached = true;
                }
            }
            this.ModuleAllowed = true;
            this.UpdateMap();
        }

        private static MgClassDefinition CreateFieldsOrderClass()
    {
        MgClassDefinition classDef = new MgClassDefinition {
            Name = "FIELDS_ORDER"
        };
       
        MgPropertyDefinitionCollection propCollection = classDef.GetProperties();
        MgPropertyDefinition propIdDef = HtzSdfHelper.CreateIdProperty("ID");
        propCollection.Add(propIdDef);
        classDef.GetIdentityProperties().Add(propIdDef);
        propCollection.Add(HtzSdfHelper.CreateIntProperty("ORDER_ID"));
        propCollection.Add(HtzSdfHelper.CreateTextProperty("FEAT_CLASS_NAME", 0x19));
        propCollection.Add(HtzSdfHelper.CreateTextProperty("PROPERTY_NAME", 0x19));
        return classDef;
    }

        private void CreateLayer(MgResourceIdentifier layerResId, string filter)
    {
        XmlDocument doc = new XmlDocument {
            PreserveWhitespace = true
        };
       
        string path = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "templates/Maintenance.xml");
        doc.Load(path);
        XmlNode featureSourceNode = doc.GetElementsByTagName("ResourceId")[0];
        XmlText resContent = doc.CreateTextNode(this._sdfResId.ToString());
        featureSourceNode.AppendChild(resContent);
        XmlNode filterNode = doc.GetElementsByTagName("Filter")[0];
        XmlText filterContent = doc.CreateTextNode(filter);
        filterNode.AppendChild(filterContent);
        ResourceHelper.SetResourceFromXml(this._helper, layerResId, doc);
    }

        private static MgClassDefinition CreateMaintenanceClass(bool isLog)
    {
        MgClassDefinition classDef = new MgClassDefinition {
            Name = isLog ? "MaintenanceLog" : "Maintenance"
        };
       
        MgPropertyDefinitionCollection propCollection = classDef.GetProperties();
        MgPropertyDefinition propIdDef = HtzSdfHelper.CreateIdProperty();
        propCollection.Add(propIdDef);
        classDef.GetIdentityProperties().Add(propIdDef);
        if (isLog)
        {
            propCollection.Add(HtzSdfHelper.CreateIntProperty("ItemId"));
        }
        else
        {
            propCollection.Add(HtzSdfHelper.CreateTextProperty("FeatureSource", 0xff));
            propCollection.Add(HtzSdfHelper.CreateIntProperty("UID"));
        }
        propCollection.Add(HtzSdfHelper.CreateTextProperty("InfrastructureType", 50));
        propCollection.Add(HtzSdfHelper.CreateTextProperty("OperationType", 50));
        propCollection.Add(HtzSdfHelper.CreateTextProperty("OperationDescription", 0xff));
        propCollection.Add(HtzSdfHelper.CreateTextProperty("Status", 50));
        propCollection.Add(HtzSdfHelper.CreateDateTimeProperty("dtStart"));
        propCollection.Add(HtzSdfHelper.CreateDateTimeProperty("dtDue"));
        propCollection.Add(HtzSdfHelper.CreateDateTimeProperty("dtEnd"));
        propCollection.Add(HtzSdfHelper.CreateDateTimeProperty("dtUpdate"));
        propCollection.Add(HtzSdfHelper.CreateTextProperty("Contractor", 50));
        propCollection.Add(HtzSdfHelper.CreateTextProperty("Inspector", 50));
        propCollection.Add(HtzSdfHelper.CreateIntProperty("WorkCost"));
        propCollection.Add(HtzSdfHelper.CreateIntProperty("OrderNum"));
        propCollection.Add(HtzSdfHelper.CreateTextProperty("Note", 0xff));
        if (!isLog)
        {
            propCollection.Add(HtzSdfHelper.CreateGeometricProperty("Main"));
            classDef.DefaultGeometryPropertyName = "Geometry";
        }
        return classDef;
    }

        private static XmlNode CreateMaintenanceGroup(XmlDocument doc, string infrastructureGroupName)
        {
            XmlElement groupNode = doc.CreateElement("MapLayerGroup");
            groupNode.AppendChild(CreateNode(doc, "Name", "Maintenance"));
            groupNode.AppendChild(CreateNode(doc, "Visible", "false"));
            groupNode.AppendChild(CreateNode(doc, "ShowInLegend", "true"));
            groupNode.AppendChild(CreateNode(doc, "ExpandInLegend", "false"));
            groupNode.AppendChild(CreateNode(doc, "LegendLabel", "תחזוקה"));
            groupNode.AppendChild(CreateNode(doc, "Group", infrastructureGroupName));
            doc.SelectSingleNode("MapDefinition").InsertAfter(groupNode, doc.SelectSingleNode(string.Format("MapDefinition/MapLayerGroup[Name = \"{0}\"]", infrastructureGroupName)));
            return groupNode;
        }

        private void CreateMaintenanceLayerNode(XmlDocument doc, string resId, string name, string legendLabel, string groupName)
        {
            if ((bool)this._config["ShowMaintFor" + name.Replace("Maint_", string.Empty)].Value)
            {
                XmlElement layerNode = doc.CreateElement("MapLayer");
                layerNode.AppendChild(CreateNode(doc, "Name", name));
                layerNode.AppendChild(CreateNode(doc, "ResourceId", resId));
                layerNode.AppendChild(CreateNode(doc, "Selectable", "false"));
                layerNode.AppendChild(CreateNode(doc, "ShowInLegend", "true"));
                layerNode.AppendChild(CreateNode(doc, "LegendLabel", legendLabel));
                layerNode.AppendChild(CreateNode(doc, "ExpandInLegend", "false"));
                layerNode.AppendChild(CreateNode(doc, "Visible", "true"));
                layerNode.AppendChild(CreateNode(doc, "Group", groupName));
                doc.SelectSingleNode("MapDefinition").InsertBefore(layerNode, doc.SelectSingleNode("MapDefinition/MapLayer"));
            }
        }

        public void CreateNew(int uid)
        {
            this.CreateNew(uid, this.GetInfrastuctureType(this._parentLayer.FeatureSourceId));
        }

        public void CreateNew(int uid, string infrastructureType)
        {
            if (!string.IsNullOrEmpty(infrastructureType) && (infrastructureType != "בחר מרשימה"))
            {
                SetInfrastructureType(this._parentLayer.FeatureSourceId, infrastructureType);
                MgGeometryProperty geometry = null;
                MgResourceIdentifier resId = new MgResourceIdentifier(this._parentLayer.FeatureSourceId);
                MgFeatureQueryOptions opt = new MgFeatureQueryOptions();
                opt.SetFilter(string.Format("{0}={1}", this._uidKey, uid));
                MgFeatureReader reader = this._helper.FeatureService.SelectFeatures(resId, this._parentLayer.FeatureClassName, opt);
                MgClassDefinition classDefinition = this._helper.FeatureService.GetClassDefinition(resId, this._parentLayer.FeatureClassName.Split(new char[] { ':' })[0], this._parentLayer.FeatureClassName.Split(new char[] { ':' })[1]);
                if (classDefinition != null)
                {
                    string geomName = classDefinition.DefaultGeometryPropertyName;
                    if (reader.ReadNext())
                    {
                        MgByteReader value = reader.GetGeometry(geomName);
                        if (value != null)
                        {
                            geometry = new MgGeometryProperty(geomName, value);
                        }
                    }
                }
                reader.Close();
                reader.Dispose();
                if (geometry != null)
                {
                    MgDateTime today = new MgDateTime();
                    MgPropertyCollection properties = new MgPropertyCollection {
                    geometry,
                    new MgInt32Property("UID", uid),
                    new MgStringProperty("InfrastructureType", infrastructureType),
                    new MgStringProperty("OperationType", "לא מוגדר"),
                    new MgStringProperty("Status", "פתוח"),
                    new MgStringProperty("FeatureSource", this._parentLayer.FeatureSourceId),
                    new MgDateTimeProperty("dtStart", today),
                    new MgDateTimeProperty("dtDue", today),
                    new MgDateTimeProperty("dtUpdate", today)
                };
                    MgInsertFeatures insert = new MgInsertFeatures("Maintenance", properties);
                    MgFeatureCommandCollection commands = new MgFeatureCommandCollection {
                    insert
                };
                    this._helper.FeatureService.UpdateFeatures(this._sdfResId, commands, false);
                }
            }
        }

        private static XmlNode CreateNode(XmlDocument doc, string name, string innerText)
        {
            XmlNode node = doc.CreateElement(name);
            node.InnerText = innerText;
            return node;
        }

        private static MgPropertyCollection CreateOrderEntry(string propertyName, int orderNum)
        {
            return new MgPropertyCollection { new MgInt32Property("ORDER_ID", orderNum), new MgStringProperty("FEAT_CLASS_NAME", "Maintenance"), new MgStringProperty("PROPERTY_NAME", propertyName) };
        }

        private void CreateSdf(MgResourceIdentifier sdfResId)
        {
            MgFeatureSchema schema = new MgFeatureSchema("Maintenance", "");
            MgCreateSdfParams sdfParams = new MgCreateSdfParams("Main", this._helper.Map.MapSRS, schema);
            schema.GetClasses().Add(CreateMaintenanceClass(false));
            schema.GetClasses().Add(CreateMaintenanceClass(true));
            schema.GetClasses().Add(CreateFieldsOrderClass());
            this._helper.FeatureService.CreateFeatureSource(sdfResId, sdfParams);
            this.SetOrder();
        }

        public void DeleteFeature(int featId)
        {
            MgDeleteFeatures delete = new MgDeleteFeatures("Maintenance", string.Format("FeatId={0}", featId));
            MgDeleteFeatures deleteLog = new MgDeleteFeatures("MaintenanceLog", string.Format("ItemId={0}", featId));
            MgFeatureCommandCollection commands = new MgFeatureCommandCollection {
            delete,
            deleteLog
        };
            this._helper.FeatureService.UpdateFeatures(this._sdfResId, commands, false);
        }

        private static XmlDocument GetConfigurationDocument()
        {
            XmlDocument doc = new XmlDocument();
            string path = Path.Combine(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, ConfigurationManager.AppSettings["XmlDataFolder"]), "MaintenanceConfig.xml");
            if (!File.Exists(path))
            {
                XmlElement root = doc.CreateElement("Configuration");
                XmlElement infrastructures = doc.CreateElement("Infrastructures");
                XmlElement infraTypes = doc.CreateElement("InfrastructureTypes");
                root.AppendChild(infraTypes);
                root.AppendChild(infrastructures);
                doc.AppendChild(root);
                return doc;
            }
            doc.Load(path);
            return doc;
        }

        public string GetInfrastuctureType(string resId)
        {
            string result = string.Empty;
            XmlNode node = GetConfigurationDocument().SelectSingleNode(string.Format("Configuration/Infrastructures/Infrastructure[@resId=\"{0}\"]", resId));
            if (node != null)
            {
                result = node.Attributes["type"].InnerText;
            }
            return result;
        }

        private void InitDefaultInfraTypes()
        {
            this._infraTypes.Add("Water", "מים");
            this._infraTypes.Add("Sewage", "ביוב");
            this._infraTypes.Add("Drainage", "ניקוז");
            this._infraTypes.Add("Lighting", "תאורה");
            this._infraTypes.Add("Elec", "חשמל");
            this._infraTypes.Add("Comm", "תקשורת");
            this._infraTypes.Add("Other", "אחר");
            XmlDocument doc = GetConfigurationDocument();
            XmlNodeList types = doc.SelectNodes("Configuration/InfrastructureTypes/InfrastructureType");
            if ((types != null) && (types.Count > 0))
            {
                this._infraTypes = new Dictionary<string, string>();
                foreach (XmlNode t in types)
                {
                    this._infraTypes.Add(t.Attributes["Name"].InnerText, t.Attributes["Alias"].InnerText);
                }
            }
            else
            {
                XmlNode tt = doc.SelectSingleNode("Configuration/InfrastructureTypes");
                foreach (KeyValuePair<string, string> t in this._infraTypes)
                {
                    XmlElement item = doc.CreateElement("InfrastructureType");
                    XmlAttribute name = doc.CreateAttribute("Name");
                    name.InnerText = t.Key;
                    XmlAttribute alias = doc.CreateAttribute("Alias");
                    alias.InnerText = t.Value;
                    item.Attributes.Append(name);
                    item.Attributes.Append(alias);
                    tt.AppendChild(item);
                }
            }
            SaveInfraTypesDocument(doc);
        }

        private static void SaveInfraTypesDocument(XmlDocument doc)
        {
            string path = Path.Combine(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, ConfigurationManager.AppSettings["XmlDataFolder"]), "MaintenanceConfig.xml");
            doc.Save(path);
        }

        private static void SetInfrastructureType(string resId, string infraType)
        {
            XmlDocument doc = GetConfigurationDocument();
            XmlNode node = doc.SelectSingleNode(string.Format("Configuration/Infrastructures/Infrastructure[@resId=\"{0}\"]", resId));
            if (node != null)
            {
                node.Attributes["type"].InnerText = infraType;
            }
            else
            {
                node = doc.CreateElement("Infrastructure");
                XmlAttribute resIdAttr = doc.CreateAttribute("resId");
                resIdAttr.InnerText = resId;
                node.Attributes.Append(resIdAttr);
                XmlAttribute infraTypeAttr = doc.CreateAttribute("type");
                infraTypeAttr.InnerText = infraType;
                node.Attributes.Append(infraTypeAttr);
                doc.SelectSingleNode("Configuration/Infrastructures").AppendChild(node);
            }
            SaveInfraTypesDocument(doc);
        }

        private void SetOrder()
        {
            int index = 1;
            MgFeatureCommandCollection cmdCollection = new MgFeatureCommandCollection();
            MgBatchPropertyCollection batchPropertyCollection = new MgBatchPropertyCollection {
            CreateOrderEntry("FeatureSource", 0),
            CreateOrderEntry("UID", 0),
            CreateOrderEntry("InfrastructureType", index++),
            CreateOrderEntry("OperationType", index++),
            CreateOrderEntry("OperationDescription", index++),
            CreateOrderEntry("Status", index++),
            CreateOrderEntry("Contractor", index++),
            CreateOrderEntry("Inspector", index++),
            CreateOrderEntry("OrderNum", index++),
            CreateOrderEntry("dtStart", index++),
            CreateOrderEntry("dtDue", index++),
            CreateOrderEntry("dtEnd", index++),
            CreateOrderEntry("dtUpdate", index++),
            CreateOrderEntry("WorkCost", index++),
            CreateOrderEntry("Note", index)
        };
            MgInsertFeatures insert = new MgInsertFeatures("Maintenance:FIELDS_ORDER", batchPropertyCollection);
            cmdCollection.Add(insert);
            this._helper.FeatureService.UpdateFeatures(this._sdfResId, cmdCollection, false);
        }

        private void UpdateMap()
        {
            XmlDocument doc = ResourceHelper.GetResourceXml(this._helper, this._helper.Map.MapDefinition);
            XmlNode infrastructureGroupNode = doc.SelectSingleNode("MapDefinition/MapLayerGroup[LegendLabel=\"תשתיות\"]");
            if (infrastructureGroupNode != null)
            {
                string infrastructureGroupName = infrastructureGroupNode.SelectSingleNode("Name").InnerText;
                XmlNode maintenanceGroupNode = doc.SelectSingleNode("MapDefinition/MapLayerGroup[LegendLabel=\"תחזוקה\"]") ?? CreateMaintenanceGroup(doc, infrastructureGroupName);
                string maintenanceGroupName = maintenanceGroupNode.SelectSingleNode("Name").InnerText;
                for (int i = this._layersResId.Count - 1; i >= 0; i--)
                {
                    if (doc.SelectSingleNode(string.Format("MapDefinition/MapLayer[Name = \"{0}\"]", this._layersResId[i].Name)) == null)
                    {
                        string legendLabel = this._infraTypes[this._layersResId[i].GetName().Split(new char[] { '_' })[1]];
                        string resId = this._layersResId[i].ToString();
                        string name = this._layersResId[i].Name;
                        this.CreateMaintenanceLayerNode(doc, resId, name, legendLabel, maintenanceGroupName);
                        this._newLayerAttached = true;
                    }
                }
                ResourceHelper.SetResourceFromXml(this._helper, this._helper.Map.MapDefinition, doc);
            }
        }

        // Properties
        public Dictionary<string, string> InfrastructureTypes
        {
            get
            {
                return this._infraTypes;
            }
        }

        public bool ModuleAllowed { get; set; }

        public bool NewLayerAttached
        {
            get
            {
                return this._newLayerAttached;
            }
            set
            {
                this._newLayerAttached = value;
            }
        }
    }


}